package br.requisiteworks.seguranca.modelo.servico;

import java.util.List;

import javax.annotation.PostConstruct;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import br.requisiteworks.modelo.Funcionario;
import br.requisiteworks.modelo.dao.FuncionarioDao;
import br.requisiteworks.seguranca.modelo.Funcao;
import br.requisiteworks.seguranca.modelo.Usuario;
import br.requisiteworks.seguranca.modelo.dao.FuncaoDao;
import br.requisiteworks.seguranca.modelo.dao.UserDao;
import br.requisiteworks.util.persistencia.ErroBancoDados;
import br.requisiteworks.util.persistencia.ResultadoVazio;

@Service("AuthenticationProviderService")
@Repository
public class AuthenticationProviderService implements AuthenticationProvider {

	private static final Log log = LogFactory
			.getLog(AuthenticationProviderService.class);

	@Autowired
	private UserDao userDao;

	@Autowired
	private FuncionarioDao funcionarioDao;

	@Autowired
	private FuncaoDao funcaoDao;

	@PostConstruct
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void cadastraUsuarioPadrao() {

		log.info("Verificando usuario padrao \"admin\"");

		List<Funcionario> usersList;
		Funcionario admin;
		try {
			usersList = funcionarioDao.select("admin");
			admin = usersList.get(0);
		} catch (ResultadoVazio e) {
			log.info("Cadastrando funcionario \"admin\"");
			admin = new Funcionario();
			admin.setUsuarioRegistro("admin");
			admin.setEmail("admin@mail.com");
			admin.setNome("admin");
			admin.setTelefone("+55 11 42568798");
			try {
				funcionarioDao.insert(admin);
			} catch (ErroBancoDados e1) {
				log.error("Erro ao cadastrar funcionario", e);
			}
		}

		log.info("Carregando usuario padrao \"admin\"");

		try {

			userDao.select(admin);

		} catch (ResultadoVazio e) {
			try {
				Usuario adminUser;
				log.info("Cadastrando usuario padrao \"admin\"");

				adminUser = new Usuario();
				adminUser.setUsername("admin");
				adminUser.setPassword("admin");
				adminUser.setFuncionario(admin);

				Funcao funcao = new Funcao("GERENTE");
				funcao.setUsuarioRegistro("admin");
				funcaoDao.insert(funcao);
				adminUser.addAuthority(funcao);

				funcao = new Funcao("ANALISTA");
				funcao.setUsuarioRegistro("admin");
				funcaoDao.insert(funcao);
				adminUser.addAuthority(funcao);

				funcao = new Funcao("USUARIO");
				funcao.setUsuarioRegistro("admin");
				funcaoDao.insert(funcao);
				adminUser.addAuthority(funcao);

				funcao = new Funcao("ADMINISTRADOR");
				funcao.setUsuarioRegistro("admin");
				funcaoDao.insert(funcao);
				adminUser.addAuthority(funcao);

				adminUser.setUsuarioRegistro("admin");

				userDao.insert(adminUser);
			} catch (ErroBancoDados ex) {
				log.error(ex);
			}
		}
		log.info("Usuario padrao OK");
	}

	@Override
	public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {

		log.info("Autenticando usuario: " + authentication.getName());

		try {
			Usuario user = userDao.select(authentication.getName());

			if (user.getPassword().equals(authentication.getCredentials())) {
				log.info("Usuario autenticado: " + user);
				user.setAuthenticated(true);
			} else {
				log.info("Usuario invalido: " + user);
				user.setAuthenticated(false);
				throw new BadCredentialsException("incorrect password");
			}

			return user;
		} catch (ResultadoVazio e) {
			authentication.setAuthenticated(false);
			log.info("Usuario invalido");
			throw new UsernameNotFoundException(authentication.getName());
		}
	}

	@Override
	public boolean supports(Class<? extends Object> class1) {
		return !class1.isAssignableFrom(Usuario.class);
	}

}
